package com.ace.user;

import java.util.ArrayList;
import java.util.Collection;

import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;

import com.common.model.PagedList;
import com.common.rest.error.InvalidRequestException;

@Path("/users")
public class UserService {
	
	private UserMapper mapper;

	public UserMapper getMapper() {
		return mapper;
	}

	public void setMapper(UserMapper mapper) {
		this.mapper = mapper;
	}
	
	private JdbcUserDetailsManager userManager;
	
	private PasswordEncoder passwordEncoder;

	public JdbcUserDetailsManager getUserManager() {
		return userManager;
	}

	public void setUserManager(JdbcUserDetailsManager userManager) {
		this.userManager = userManager;
	}
	
	public PasswordEncoder getPasswordEncoder() {
		return passwordEncoder;
	}

	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
		this.passwordEncoder = passwordEncoder;
	}

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public PagedList getAllUsers(@QueryParam("start") int start,@QueryParam("length") int pageSize) {
		if(start < 0) {
			start = 0;
		}
		if(pageSize <= 0) {
			pageSize = 10;
		}
		PagedList data = new PagedList();
		data.setData(mapper.getAllUsers(start, pageSize));
		data.setCount(mapper.getUserCount());
		return data;
	}
	
	@POST
	public Response addUser(UserHolder temp) {
		try {
			Collection<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
			roles.add(new SimpleGrantedAuthority("User"));
			if(temp.isAdmin()) {
				roles.add(new SimpleGrantedAuthority("Admin"));
			}
			User user = new User(temp.getUsername(), passwordEncoder.encode(temp.getPassword()), roles);
			userManager.createUser(user);
		} catch(DuplicateKeyException ex) {
			throw new InvalidRequestException("R001", "User name already used");
		} catch (DataAccessException ex) {
			throw new InvalidRequestException("R002", "Unable to register", ex);
		}
		return Response.ok().build();
	}
	
	@PUT
	public Response updateUser(UserHolder temp) {
		try {
			Collection<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
			roles.add(new SimpleGrantedAuthority("User"));
			if(temp.isAdmin()) {
				roles.add(new SimpleGrantedAuthority("Admin"));
			}
			User user = new User(temp.getUsername(), passwordEncoder.encode(temp.getPassword()), roles);
			userManager.updateUser(user);
		} catch (DataAccessException ex) {
			throw new InvalidRequestException("R002", "Unable to update user", ex);
		}
		return Response.ok().build();
	}
	
	@PUT
	@Path("/{username}/enable")
	public Response enableUser(@PathParam("username") String username) {
		int result = mapper.enableUser(username);
		if(result == 1) {
			return Response.ok().build();
		}
		throw new InvalidRequestException("", "User not found");
	}
	
	@PUT
	@Path("/{username}/disable")
	public Response disableUser(@PathParam("username") String username) {
		int result = mapper.disableUser(username);
		if(result == 1) {
			return Response.ok().build();
		}
		throw new InvalidRequestException("", "User not found");
	}
	
	@PUT
	@Path("/current/changePassword")
	public Response changePassword(@FormParam("newPassword")String password) {
		UserDetails user = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		String username = user.getUsername();
		int result = mapper.changePassword(username, passwordEncoder.encode(password));
		if(result == 1) {
			return Response.ok().build();
		}
		throw new InvalidRequestException("", "Failed to change password");
	}
	
	public static class UserHolder {
		
		private String username, password;
		private boolean admin;

		public String getUsername() {
			return username;
		}

		public void setUsername(String username) {
			this.username = username;
		}

		public String getPassword() {
			return password;
		}

		public void setPassword(String password) {
			this.password = password;
		}
		
		public boolean isAdmin() {
			return admin;
		}

		public void setAdmin(boolean admin) {
			this.admin = admin;
		}
	}
}
